/*-
 * Copyright 2012 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#include "sys.h"
#include "cdc256.h"
//---------------------------------------------------------------------------
static const struct cdc256_data prime = {
  UINT64_C(0x992E367BE6F0EA1E),
  UINT64_C(0x71DCF41FFACC283F),
  UINT64_C(0xC9581F48D85ABD75),
  UINT64_C(0xE4B93335FF1CE990),
  UINT64_C(0xE51D6424EFEC1E01),
  UINT64_C(0x353867A0E66C2A39),
  UINT64_C(0xA8DBF7B782226B67),
  UINT64_C(0x9F8B7F0DC254488E),
  0
};
//---------------------------------------------------------------------------
static __inline__ void shuffle(struct cdc256_data * v)
{
  v->a -= v->e; v->f ^= v->h >>  9; v->h += v->a;
  v->b -= v->f; v->g ^= v->a <<  9; v->a += v->b;
  v->c -= v->g; v->h ^= v->b >> 23; v->b += v->c;
  v->d -= v->h; v->a ^= v->c << 15; v->c += v->d;
  v->e -= v->a; v->b ^= v->d >> 14; v->d += v->e;
  v->f -= v->b; v->c ^= v->e << 20; v->e += v->f;
  v->g -= v->c; v->d ^= v->f >> 17; v->f += v->g;
  v->h -= v->d; v->e ^= v->g << 14; v->g += v->h;
}
//---------------------------------------------------------------------------
static __inline__
void update(struct cdc256_data * a,const struct cdc256_data * v)
{
  a->a -= v->e; a->f ^= v->h >>  9; a->h += v->a;
  a->b -= v->f; a->g ^= v->a <<  9; a->a += v->b;
  a->c -= v->g; a->h ^= v->b >> 23; a->b += v->c;
  a->d -= v->h; a->a ^= v->c << 15; a->c += v->d;
  a->e -= v->a; a->b ^= v->d >> 14; a->d += v->e;
  a->f -= v->b; a->c ^= v->e << 20; a->e += v->f;
  a->g -= v->c; a->d ^= v->f >> 17; a->f += v->g;
  a->h -= v->d; a->e ^= v->g << 14; a->g += v->h;
}
//---------------------------------------------------------------------------
struct cdc256_data * cdc256_new(void)
{
  struct cdc256_data * code = salloc(sizeof(struct cdc256_data),NULL);
  if( code != NULL )
    cdc256_init(code);
  return code;
}
//---------------------------------------------------------------------------
void cdc256_delete(struct cdc256_data * code)
{
  sfree(code);
}
//---------------------------------------------------------------------------
void cdc256_init(struct cdc256_data * code)
{
  static const struct cdc256_data init_value = {
    UINT64_C(0xA640524A5B44F1FC),
    UINT64_C(0xC535059705F0BB7E),
    UINT64_C(0xC8ED76CF6B6EA626),
    UINT64_C(0x531D1E8E254EA59E),
    UINT64_C(0x8C0FE7F3E46E2A80),
    UINT64_C(0x1C53F41FD1E3A7F8),
    UINT64_C(0x08D4DEAAA1C33335),
    UINT64_C(0x4C592980FBE9B011),
    0
  };
  *code = init_value;
}
//---------------------------------------------------------------------------
void cdc256_update(struct cdc256_data * code,const void * data,uintptr_t size)
{
  struct cdc256_data pad;

  code->position += size;

  while( size > 0 ){
    if( size < 64 ){
      memcpy(&pad,data,size);
      memset((uint8_t *) &pad + size,0,64 - size);
      data = &pad;
    }
    update(code,(const struct cdc256_data *) data);
    shuffle(code);
    if( size < 64 ) break;
    data = (const uint8_t *) data + 64;
    size -= 64;
  }

  pad.a = pad.b = pad.c = pad.d = pad.e = pad.f = pad.g = pad.h = code->position;

  shuffle(&pad);
  shuffle(&pad);

  update(code,&pad);

  shuffle(code);
}
//---------------------------------------------------------------------------
void cdc256_final(struct cdc256_data * code,struct cdc256 * cdc256)
{
  uintptr_t c;
  const uint8_t * p = (const uint8_t *) code;

  for( c = 0; c < 4; c++, p += 16 ){
    uint64_t h = 5381;
    h = (h << 5) + h + p[0];
    h = (h << 5) + h + p[1];
    h = (h << 5) + h + p[2];
    h = (h << 5) + h + p[3];
    h = (h << 5) + h + p[4];
    h = (h << 5) + h + p[5];
    h = (h << 5) + h + p[6];
    h = (h << 5) + h + p[7];
    h = (h << 5) + h + p[8];
    h = (h << 5) + h + p[9];
    h = (h << 5) + h + p[10];
    h = (h << 5) + h + p[11];
    h = (h << 5) + h + p[12];
    h = (h << 5) + h + p[13];
    h = (h << 5) + h + p[14];
    h = (h << 5) + h + p[15];
    cdc256->d64[c] = h;
  }
}
//---------------------------------------------------------------------------
void cdc256_print(FILE * stream,const struct cdc256 * code)
{
  fprintf(stream,
    "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X,\n"
    "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X,\n"
    "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X,\n"
    "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
    code->data[0],
    code->data[1],
    code->data[2],
    code->data[3],
    code->data[4],
    code->data[5],
    code->data[6],
    code->data[7],
    code->data[8],
    code->data[9],
    code->data[10],
    code->data[11],
    code->data[12],
    code->data[13],
    code->data[14],
    code->data[15],
    code->data[16],
    code->data[17],
    code->data[18],
    code->data[19],
    code->data[20],
    code->data[21],
    code->data[22],
    code->data[23],
    code->data[24],
    code->data[25],
    code->data[26],
    code->data[27],
    code->data[28],
    code->data[29],
    code->data[30],
    code->data[31]
  );
}
//---------------------------------------------------------------------------
